/*
 * Copyright (C) 2014 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "TiledBuffer.h"

namespace WebCore {

MemcpyThread& TiledBuffer::memcpyThread()
{
    static MemcpyThread s_memcpyThread("SceManxTileCopyThread");
    return s_memcpyThread;
}

IntRect TiledBuffer::tileRect(const IntSize& size, const IntPoint& coordinate)
{
    return IntRect(
        coordinate.x() * size.width(),
        coordinate.y() * size.height(),
        size.width(),
        size.height());
}

FloatRect TiledBuffer::scaledRect(const IntSize& size, const IntPoint& coordinate, float scale)
{
    return FloatRect(
        coordinate.x() * size.width() / scale,
        coordinate.y() * size.height() / scale,
        size.width() / scale,
        size.height() / scale);
}

IntRect TiledBuffer::enclosingScaledRect(const IntSize& size, const IntPoint& coordinate, float scale)
{
    return enclosingIntRect(scaledRect(size, coordinate, scale));
}

IntRect TiledBuffer::tileRect() const
{
    return tileRect(tileSize(), m_coordinate);
}

FloatRect TiledBuffer::scaledRect() const
{
    return scaledRect(tileSize(), m_coordinate, m_contentsScale);
}

IntRect TiledBuffer::enclosingScaledRect() const
{
    return enclosingScaledRect(tileSize(), m_coordinate, m_contentsScale);
}

IntRect TiledBuffer::targetRectForPartialRepaint() const
{
    FloatRect scaledDirtyRect = m_dirtyRect;
    scaledDirtyRect.scale(m_contentsScale);

    IntRect ret = tileRect();
    ret.intersect(enclosingIntRect(scaledDirtyRect));
    return ret;
}

PassRefPtr<TiledBuffer> TiledBuffer::create(const Coordinate& coordinate, TilePool* pool)
{
    TiledBuffer* that = 0;
    tilebackend::Texture texture = pool->allocateTileTexture();
    if (texture.buffer)
        that = new TiledBuffer(coordinate, texture, pool);

    if (that)
        that->setValid(false);
    return adoptRef(that);
}

TiledBuffer::TiledBuffer(const Coordinate& coordinate, tilebackend::Texture& texture, TilePool* pool)
    : m_refCount(1)
    , m_coordinate(coordinate)
    , m_contentsScale(1.0f)
    , m_pool(pool)
{
    m_texture = texture;
    texture.buffer = 0;

    m_contentsScale = 1.0;
}

TiledBuffer::~TiledBuffer()
{
    m_pool->freeTileTexture(m_texture);
}

void TiledBuffer::duplicateAsync(TiledBuffer* that)
{
    ASSERT(m_texture.width == that->m_texture.width);
    ASSERT(m_texture.height == that->m_texture.height);
    ASSERT(m_texture.format == that->m_texture.format);
    ASSERT(m_texture.stride == that->m_texture.stride);

    m_contentsScale = that->m_contentsScale;
    m_isValid = that->m_isValid;
    m_dirtyRect = that->m_dirtyRect;

    MemcpyThread::Argument arg;
    uint32_t height = 0;
    arg.dest = m_texture.buffer;
    arg.src = that->m_texture.buffer;
    height = that->m_texture.height;

    // Simple optimization.
    IntRect target = targetRectForPartialRepaint();
    if (target.width() == m_texture.width) {
        uint32_t y = (target.y() - m_coordinate.y() * m_texture.height);
        if (y + target.height() <= m_texture.height) { /* Avoid unexpected behavior with broken value */
            if (y > 0 && y + target.height() == m_texture.height) {
                // Duplicate only upper area.
                height = y;
            } else if (!y) {
                // Duplicate only lower area.
                uint32_t offset = target.height() * m_texture.stride;
                arg.dest = (reinterpret_cast<uint8_t*>(m_texture.buffer) + offset);
                arg.src = (reinterpret_cast<uint8_t*>(that->m_texture.buffer) + offset);
                height -= target.height();
            }
        }
    }

    arg.size = height * m_texture.stride;
    if (arg.size)
        memcpyThread().push(arg);
}

void TiledBuffer::waitDuplicationCompleted()
{
    memcpyThread().wait();
}

} // namespace WebCore
